// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "Step.h"
#include "ChildView.h"
#include "LineDlg.h"


#include "SgRegularCylinder.h"
#include "SgRegularPolygon.h"
#include "SgTranslate.h"
#include "SgRotateAroundPoint.h"
#include "SgTexture.h"
#include <cmath>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const double PI = std::atan(1.0)*4;


// CChildView

CChildView::CChildView()
{
	m_starx = 0.5;
	m_stary = 0.5;
	m_starradius = 0.2;
	m_starrotation = 0.0;

	m_red = 24;
	m_green = 69;
	m_blue = 59;

	m_spinAngle = 0;
	m_spinTimer = 0;

	SetDoubleBuffer(true);

    m_camera.Set(20, 10, 50, 0, 0, 0, 0, 1, 0);
    m_camera.MouseMode(CGrCamera::PITCHYAW);

	

	m_park = new CSgComposite();

	m_environment = new CEnvironmentFactory();
	m_environment->Create(m_park);

	// Factories will be created (and will create the rides) here
 //   m_roundUp = new CRoundUpFactory();
	//m_roundUp->SetTimeline(&m_timeline);
	//m_roundUp->Create(m_park);

	//m_scrams = new CScrambledEggsFactory();
	//m_scrams->SetTimeline(&m_timeline);
	//m_scrams->Create(m_park);

	m_round = new CMerryRoundFactory();
	m_round->SetTimeline(&m_timeline);
	m_round->Create(m_park);

	
	//m_pirateShip = new CPirateShipFactory();
	//m_pirateShip->SetTimeline(&m_timeline);
	//m_pirateShip->Create(m_park);

	//m_goCart = new CGoCartFactory();
	//m_goCart->SetTimeline(&m_timeline);
	//m_goCart->Create(m_park);

}

CChildView::~CChildView()
{
	delete m_environment;
	delete m_roundUp;
	delete m_scrams;
	delete m_round;
	delete m_pirateShip;
	delete m_goCart;

	delete m_park;
}


BEGIN_MESSAGE_MAP(CChildView, COpenGLWnd)
	ON_WM_PAINT()
	ON_COMMAND(ID_MOVEMENT_SPIN, &CChildView::OnMovementSpin)
	ON_WM_TIMER()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_COMMAND(ID_CAMERA_PAN, &CChildView::OnCameraPan)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_PAN, &CChildView::OnUpdateCameraPan)
	ON_COMMAND(ID_CAMERA_PITCH, &CChildView::OnCameraPitch)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_PITCH, &CChildView::OnUpdateCameraPitch)
	ON_COMMAND(ID_Menu, &CChildView::OnCameraRollMove)
	ON_UPDATE_COMMAND_UI(ID_Menu, &CChildView::OnUpdateCameraRollMove)
	ON_COMMAND(ID_CAMERA_DOLLY, &CChildView::OnCameraDolly)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_DOLLY, &CChildView::OnUpdateCameraDolly)
	ON_WM_KEYDOWN()
	ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()



// CChildView message handlers

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!COpenGLWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);

	return TRUE;
}



void CChildView::OnGLDraw(CDC * pDC)
{
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//
	// Set up the camera
	//

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Determine the screen size so we can determine the aspect ratio
	int width, height;
	GetSize(width, height);
	GLdouble aspectratio = GLdouble(width) / GLdouble(height);

	// Set the camera paramters
	gluPerspective(25.,		// Vertical FOV degrees
		aspectratio,	// The aspect ratio
		10.,			// Near clipping 40/130
		300.);			// Far clipping
	
	// Set the camera location
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	m_camera.gluLookAt();

	// Some standard paramters

	// enable depth test
	glEnable(GL_DEPTH_TEST);

	// Cull backfacing polygons
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);

	
	
	glPolygonMode(GL_FRONT, GL_FILL);

    // Enable lighting
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
	glEnable(GL_LIGHT2);

    GLfloat light[] = {0.8f, 0.8f, 0.8f, 1.0f};

    GLfloat lightpos[] = {10, 20, 20, 0.};
    glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light);

    GLfloat lightpos1[] = {-10, 30, -20, 0.};
    glLightfv(GL_LIGHT1, GL_POSITION, lightpos1);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light);
	
    GLfloat lightpos2[] = {0, -30, 0, 0.};
    glLightfv(GL_LIGHT2, GL_POSITION, lightpos2);
    glLightfv(GL_LIGHT2, GL_DIFFUSE, light);


	glPushMatrix();
	// Insert DRAWING CODE HERE

	m_park->Render();

	glPopMatrix();
	glFlush();
}



//
//        Name : Quad()
// Description : Inline function for drawing 
// a quadralateral.
//
inline void Quad(GLdouble *v1, GLdouble *v2, GLdouble *v3, GLdouble *v4)
{
    glBegin(GL_QUADS);
    glVertex3dv(v1);
    glVertex3dv(v2);
    glVertex3dv(v3);
    glVertex3dv(v4);
    glEnd();
}

//
//        Name : CChildView::Box()
// Description : Draw an arbitrary size box. p_x, 
//              p_y, and p_z are the height of
//              the box. We will use this 
//               as a common primitive.
//      Origin : The back corner is at 0, 0, 0, and
//               the box is entirely in the
//               positive octant.
//
void CChildView::Box(GLdouble p_x, GLdouble p_y, GLdouble p_z, const GLdouble *p_color)
{
    GLdouble a[] = {0., 0., p_z};
    GLdouble b[] = {p_x, 0., p_z};
    GLdouble c[] = {p_x, p_y, p_z};
    GLdouble d[] = {0., p_y, p_z};
    GLdouble e[] = {0., 0., 0.};
    GLdouble f[] = {p_x, 0., 0.};
    GLdouble g[] = {p_x, p_y, 0.};
    GLdouble h[] = {0., p_y, 0.};

    // I'm going to mess with the colors a bit so 
    // the faces will be visible in solid shading
    glColor3d(p_color[0], p_color[1], p_color[2]);
    Quad(a, b, c, d); // Front

    glColor3d(p_color[0] * 0.95, p_color[1] * 0.95, p_color[2] * 0.95);
    Quad(c, b, f, g); // Right

    glColor3d(p_color[0] * 0.85, p_color[1] * 0.85, p_color[2] * 0.85);
    Quad(h, g, f, e); // Back

    glColor3d(p_color[0] * 0.90, p_color[1] * 0.90, p_color[2] * 0.90);
    Quad(d, h, e, a); // Left

    glColor3d(p_color[0] * 0.92, p_color[1] * 0.92, p_color[2] * 0.92);
    Quad(d, c, g, h); // Top

    glColor3d(p_color[0] * 0.80, p_color[1] * 0.80, p_color[2] * 0.80);
    Quad(e, f, b, a); // Bottom
}


//
//        Name : CChildView::BarBell()
// Description : Draw an arbitrary size barbell. 
//      Origin : The back corner is at 0, 0, 0, and
//               the barbell is entirely in the
//               positive octant.
//
void CChildView::BarBell()
{
    const double RED[] = {0.7, 0.0, 0.0};

    glPushMatrix();
    glTranslated(-2.5, 0, -2.5);
    Box(5, 2, 5, RED);

    glTranslated(0, 5, 0);
    Box(5, 2, 5, RED);
    glPopMatrix();

    glPushMatrix();
    glTranslated(-0.5, 2, -0.5);
    Box(1, 3, 1, RED);
    glPopMatrix();
	glFlush();
}
void CChildView::OnMovementSpin()
{
	if(m_spinTimer == 0)
    {
        // Create the timer
        m_spinTimer = SetTimer(1, 30, NULL);
    }
    else
    {
        // Destroy the timer
        KillTimer(m_spinTimer);
        m_spinTimer = 0;
    }
}

void CChildView::OnTimer(UINT_PTR nIDEvent)
{
	//m_spinAngle += 5;       // 5 degrees every 30ms about

	m_timeline.Update();

    Invalidate();

	COpenGLWnd::OnTimer(nIDEvent);
}

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
    m_camera.MouseDown(point.x, point.y);

	COpenGLWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	if((nFlags & MK_LBUTTON) || (nFlags & MK_RBUTTON))
    {
         m_camera.MouseMove(point.x, point.y);
         Invalidate();
    }

	COpenGLWnd::OnMouseMove(nFlags, point);
}

void CChildView::OnRButtonDown(UINT nFlags, CPoint point)
{
    m_camera.MouseDown(point.x, point.y, 2);

    COpenGLWnd::OnRButtonDown(nFlags, point);
}

void CChildView::OnCameraPan()
{
	m_camera.MouseMode(CGrCamera::PANTILT);
}

void CChildView::OnUpdateCameraPan(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::PANTILT);
}

void CChildView::OnCameraPitch()
{
	m_camera.MouseMode(CGrCamera::PITCHYAW);
}

void CChildView::OnUpdateCameraPitch(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::PITCHYAW);
}

void CChildView::OnCameraRollMove()
{
	m_camera.MouseMode(CGrCamera::ROLLMOVE);
}

void CChildView::OnUpdateCameraRollMove(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::ROLLMOVE);
}

void CChildView::OnCameraDolly()
{
	m_camera.MouseMode(CGrCamera::DOLLYXY);
}

void CChildView::OnUpdateCameraDolly(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::DOLLYXY);
}



void CChildView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	CGrVector lookAt = CGrVector(m_camera.CenterX() - m_camera.EyeX(),
		m_camera.CenterY() - m_camera.EyeY(),
		m_camera.CenterZ() - m_camera.EyeZ());
	CGrVector Up = CGrVector(m_camera.UpX(), m_camera.UpY(), m_camera.UpZ());
	// lookAt cross Up
	CGrVector right = CGrVector((lookAt.Y()*Up.Z())-(Up.Y()*lookAt.Z()), 
		-(lookAt.X()*Up.Z())+(Up.X()*lookAt.Z()),
		(lookAt.X()*Up.Y())-(Up.X()*lookAt.Y()));

	// Right cross Up
	CGrVector forward = CGrVector((Up.Y()*right.Z())-(right.Y()*Up.Z()),
    (Up.X()*right.Z())+(right.X()*Up.Z()),
    (Up.X()*right.Y())-(Up.Y()*right.X()));

	// D 0x44
	// A 0x41
	// S 0x53
	// W 0x57
	// Left 0x25
	// Right 0x27
	// Down 0x28
	// Up 0x26

	right.Normalize3();
	Up.Normalize3();
	forward.Normalize3();
	lookAt.Normalize3();

	int distance = 3;
	switch(nChar)
	{
	case 0x41:		
		// A (track left)
		m_camera.SetEye(m_camera.EyeX() - (right.X() * distance), 
			m_camera.EyeY() - (right.Y() * distance), 
			m_camera.EyeZ() - (right.Z() * distance));
		m_camera.SetCenter(m_camera.CenterX() - (right.X() * distance), 
			m_camera.CenterY() - (right.Y() * distance), 
			m_camera.CenterZ() - (right.Z() * distance));
		break;
	case 0x57:
		// W (move closer)
		m_camera.SetEye(m_camera.EyeX() + (lookAt.X() * distance), 
			m_camera.EyeY() + (lookAt.Y() * distance), 
			m_camera.EyeZ() + (lookAt.Z() * distance));
		m_camera.SetCenter(m_camera.CenterX() + (lookAt.X() * distance), 
			m_camera.CenterY() + (lookAt.Y() * distance), 
			m_camera.CenterZ() + (lookAt.Z() * distance));
		break; 
	case 0x44:
		// D (track right)
		m_camera.SetEye(m_camera.EyeX() + (right.X() * distance), 
			m_camera.EyeY() + (right.Y() * distance), 
			m_camera.EyeZ() + (right.Z() * distance));
		m_camera.SetCenter(m_camera.CenterX() + (right.X() * distance), 
			m_camera.CenterY() + (right.Y() * distance), 
			m_camera.CenterZ() + (right.Z() * distance));
		break;
	case 0x53:
		// S (move further)
		m_camera.SetEye(m_camera.EyeX() - (lookAt.X() * distance), 
			m_camera.EyeY() - (lookAt.Y() * distance), 
			m_camera.EyeZ() - (lookAt.Z() * distance));
		m_camera.SetCenter(m_camera.CenterX() - (lookAt.X() * distance), 
			m_camera.CenterY() - (lookAt.Y() * distance), 
			m_camera.CenterZ() - (lookAt.Z() * distance));
		break;
	case 0x25:
		// Left (rotate left about vertical)
		m_camera.Pan(-2);
		break;
	case 0x27:
		// Right (rotate right about vertical)
		m_camera.Pan(2);
		break;
	case 0x28:
		// Down (rotate down about right vector)
		m_camera.Tilt(2);
		break;
	case 0x26:
		// Up (rotate up about right vector)
		m_camera.Tilt(-2);
		break;
	}
	Invalidate();
}


BOOL CChildView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	int distance = 3;
	CGrVector lookAt = CGrVector(m_camera.CenterX() - m_camera.EyeX(),
		m_camera.CenterY() - m_camera.EyeY(),
		m_camera.CenterZ() - m_camera.EyeZ());
	lookAt.Normalize3();
	if(zDelta > 0)
	{
		m_camera.SetEye(m_camera.EyeX() + (lookAt.X() * distance), 
			m_camera.EyeY() + (lookAt.Y() * distance), 
			m_camera.EyeZ() + (lookAt.Z() * distance));
		//m_camera.SetCenter(m_camera.CenterX() + (lookAt.X() * distance), 
		//	m_camera.CenterY() + (lookAt.Y() * distance), 
		//	m_camera.CenterZ() + (lookAt.Z() * distance));
	}
	else
	{
		m_camera.SetEye(m_camera.EyeX() - (lookAt.X() * distance), 
			m_camera.EyeY() - (lookAt.Y() * distance), 
			m_camera.EyeZ() - (lookAt.Z() * distance));
		m_camera.SetCenter(m_camera.CenterX() - (lookAt.X() * distance), 
			m_camera.CenterY() - (lookAt.Y() * distance), 
			m_camera.CenterZ() - (lookAt.Z() * distance));
	}

	Invalidate();
	return 1;
}